home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / ddj1291.zip / DFLAT.121 < prev    next >
Text File  |  1991-11-11  |  27KB  |  861 lines

  1. _C PROGRAMMING COLUMN_
  2. by Al Stevens
  3.  
  4. [LISTING ONE]
  5.  
  6. /* ------------- textbox.c ------------ */
  7.  
  8. #include "dflat.h"
  9.  
  10. #ifdef INCLUDE_SCROLLBARS
  11. static void ComputeWindowTop(WINDOW);
  12. static void ComputeWindowLeft(WINDOW);
  13. static int ComputeVScrollBox(WINDOW);
  14. static int ComputeHScrollBox(WINDOW);
  15. static void MoveScrollBox(WINDOW, int);
  16. #endif
  17. static char *GetTextLine(WINDOW, int);
  18.  
  19. #ifdef INCLUDE_SCROLLBARS
  20. int VSliding;
  21. int HSliding;
  22. #endif
  23.  
  24. /* ------------ ADDTEXT Message -------------- */
  25. static void AddTextMsg(WINDOW wnd, PARAM p1)
  26. {
  27.     /* --- append text to the textbox's buffer --- */
  28.     unsigned adln = strlen((char *)p1);
  29.     if (adln > (unsigned)0xfff0)
  30.         return;
  31.     if (wnd->text != NULL)    {
  32.         /* ---- appending to existing text ---- */
  33.         unsigned txln = strlen(wnd->text);
  34.         if ((long)txln+adln > (unsigned) 0xfff0)
  35.             return;
  36.         if (txln+adln > wnd->textlen)    {
  37.             wnd->text = realloc(wnd->text, txln+adln+3);
  38.             wnd->textlen = txln+adln+1;
  39.         }
  40.     }
  41.     else    {
  42.         /* ------ 1st text appended ------ */
  43.         wnd->text = calloc(1, adln+3);
  44.         wnd->textlen = adln+1;
  45.     }
  46.     if (wnd->text != NULL)    {
  47.         /* ---- append the text ---- */
  48.         strcat(wnd->text, (char*) p1);
  49.         strcat(wnd->text, "\n");
  50.         BuildTextPointers(wnd);
  51.     }
  52. }
  53.  
  54. /* ------------ SETTEXT Message -------------- */
  55. static void SetTextMsg(WINDOW wnd, PARAM p1)
  56. {
  57.     /* -- assign new text value to textbox buffer -- */
  58.     char *cp;
  59.     unsigned int len;
  60.     cp = (void *) p1;
  61.     len = strlen(cp)+1;
  62.     if (wnd->text == NULL || wnd->textlen < len)    {
  63.         wnd->textlen = len;
  64.         if ((wnd->text=realloc(wnd->text, len+1)) == NULL)
  65.             return;
  66.         wnd->text[len] = '\0';
  67.     }
  68.     strcpy(wnd->text, cp);
  69.     BuildTextPointers(wnd);
  70. }
  71.  
  72. /* ------------ CLEARTEXT Message -------------- */
  73. static void ClearTextMsg(WINDOW wnd)
  74. {
  75.     /* ----- clear text from textbox ----- */
  76.     if (wnd->text != NULL)
  77.         free(wnd->text);
  78.     wnd->text = NULL;
  79.     wnd->textlen = 0;
  80.     wnd->wlines = 0;
  81.     wnd->textwidth = 0;
  82.     wnd->wtop = wnd->wleft = 0;
  83.     ClearBlock(wnd);
  84.     ClearTextPointers(wnd);
  85. }
  86.  
  87. /* ------------ KEYBOARD Message -------------- */
  88. static int KeyboardMsg(WINDOW wnd, PARAM p1)
  89. {
  90.     switch ((int) p1)    {
  91.         case UP:
  92.             return SendMessage(wnd,SCROLL,FALSE,0);
  93.         case DN:
  94.             return SendMessage(wnd,SCROLL,TRUE,0);
  95.         case FWD:
  96.             return SendMessage(wnd,HORIZSCROLL,TRUE,0);
  97.         case BS:
  98.             return SendMessage(wnd,HORIZSCROLL,FALSE,0);
  99.         case PGUP:
  100.             return SendMessage(wnd,SCROLLPAGE,FALSE,0);
  101.         case PGDN:
  102.             return SendMessage(wnd,SCROLLPAGE,TRUE,0);
  103.         case CTRL_PGUP:
  104.             return SendMessage(wnd,HORIZPAGE,FALSE,0);
  105.         case CTRL_PGDN:
  106.             return SendMessage(wnd,HORIZPAGE,TRUE,0);
  107.         case HOME:
  108.             return SendMessage(wnd,SCROLLDOC,TRUE,0);
  109.         case END:
  110.             return SendMessage(wnd,SCROLLDOC,FALSE,0);
  111.         default:
  112.             break;
  113.     }
  114.     return FALSE;
  115. }
  116.  
  117. #ifdef INCLUDE_SCROLLBARS
  118. /* ------------ LEFT_BUTTON Message -------------- */
  119. static int LeftButtonMsg(WINDOW wnd, PARAM p1, PARAM p2)
  120. {
  121.     int mx = (int) p1 - GetLeft(wnd);
  122.     int my = (int) p2 - GetTop(wnd);
  123.     if (TestAttribute(wnd, VSCROLLBAR) &&
  124.                         mx == WindowWidth(wnd)-1)    {
  125.         /* -------- in the right border ------- */
  126.         if (my == 0 || my == ClientHeight(wnd)+1)
  127.             /* --- above or below the scroll bar --- */
  128.             return FALSE;
  129.         if (my == 1)
  130.             /* -------- top scroll button --------- */
  131.             return SendMessage(wnd, SCROLL, FALSE, 0);
  132.         if (my == ClientHeight(wnd))
  133.             /* -------- bottom scroll button --------- */
  134.             return SendMessage(wnd, SCROLL, TRUE, 0);
  135.         /* ---------- in the scroll bar ----------- */
  136.         if (!VSliding && my-1 == wnd->VScrollBox)    {
  137.             RECT rc;
  138.             VSliding = TRUE;
  139.             rc.lf = rc.rt = GetRight(wnd);
  140.             rc.tp = GetTop(wnd)+2;
  141.             rc.bt = GetBottom(wnd)-2;
  142.             return SendMessage(NULL, MOUSE_TRAVEL,
  143.                 (PARAM) &rc, 0);
  144.         }
  145.         if (my-1 < wnd->VScrollBox)
  146.             return SendMessage(wnd,SCROLLPAGE,FALSE,0);
  147.         if (my-1 > wnd->VScrollBox)
  148.             return SendMessage(wnd,SCROLLPAGE,TRUE,0);
  149.     }
  150.     if (TestAttribute(wnd, HSCROLLBAR) &&
  151.                         my == WindowHeight(wnd)-1) {
  152.         /* -------- in the bottom border ------- */
  153.         if (mx == 0 || my == ClientWidth(wnd)+1)
  154.             /* ------  outside the scroll bar ---- */
  155.             return FALSE;
  156.         if (mx == 1)
  157.             return SendMessage(wnd, HORIZSCROLL,FALSE,0);
  158.         if (mx == WindowWidth(wnd)-2)
  159.             return SendMessage(wnd, HORIZSCROLL,TRUE,0);
  160.         if (!HSliding && mx-1 == wnd->HScrollBox)    {
  161.             /* --- hit the scroll box --- */
  162.             RECT rc;
  163.             rc.lf = GetLeft(wnd)+2;
  164.             rc.rt = GetRight(wnd)-2;
  165.             rc.tp = rc.bt = GetBottom(wnd);
  166.             /* - keep the mouse in the scroll bar - */
  167.             SendMessage(NULL,MOUSE_TRAVEL,(PARAM)&rc,0);
  168.             HSliding = TRUE;
  169.             return TRUE;
  170.         }
  171.         if (mx-1 < wnd->HScrollBox)
  172.             return SendMessage(wnd,HORIZPAGE,FALSE,0);
  173.         if (mx-1 > wnd->HScrollBox)
  174.             return SendMessage(wnd,HORIZPAGE,TRUE,0);
  175.     }
  176.     return FALSE;
  177. }
  178.  
  179. /* ------------ MOUSE_MOVED Message -------------- */
  180. static int MouseMovedMsg(WINDOW wnd, PARAM p1, PARAM p2)
  181. {
  182.     int mx = (int) p1 - GetLeft(wnd);
  183.     int my = (int) p2 - GetTop(wnd);
  184.     if (VSliding)    {
  185.         /* ---- dragging the vertical scroll box --- */
  186.         if (my-1 != wnd->VScrollBox)    {
  187.             foreground = FrameForeground(wnd);
  188.             background = FrameBackground(wnd);
  189.             PutWindowChar(wnd, WindowWidth(wnd)-1,
  190.                     wnd->VScrollBox+1,SCROLLBARCHAR);
  191.             wnd->VScrollBox = my-1;
  192.             PutWindowChar(wnd, WindowWidth(wnd)-1,
  193.                     my, SCROLLBOXCHAR);
  194.         }
  195.         return TRUE;
  196.     }
  197.     if (HSliding)    {
  198.         /* --- dragging the horizontal scroll box --- */
  199.         if (mx-1 != wnd->HScrollBox)    {
  200.             foreground = FrameForeground(wnd);
  201.             background = FrameBackground(wnd);
  202.             PutWindowChar(wnd, wnd->HScrollBox+1,
  203.                     WindowHeight(wnd)-1, SCROLLBARCHAR);
  204.             wnd->HScrollBox = mx-1;
  205.             PutWindowChar(wnd, mx, WindowHeight(wnd)-1,
  206.                     SCROLLBOXCHAR);
  207.         }
  208.         return TRUE;
  209.     }
  210.     return FALSE;
  211. }
  212.  
  213. /* ------------ BUTTON_RELEASED Message -------------- */
  214. static void ButtonReleasedMsg(WINDOW wnd)
  215. {
  216.     if (HSliding || VSliding)    {
  217.         RECT rc;
  218.         rc.lf = rc.tp = 0;
  219.         rc.rt = SCREENWIDTH-1;
  220.         rc.bt = SCREENHEIGHT-1;
  221.         /* release the mouse ouside the scroll bar */
  222.         SendMessage(NULL, MOUSE_TRAVEL, (PARAM) &rc, 0);
  223.             VSliding ? ComputeWindowTop(wnd) :
  224.                             ComputeWindowLeft(wnd);
  225.         SendMessage(wnd, PAINT, 0, 0);
  226.         SendMessage(wnd, KEYBOARD_CURSOR, 0, 0);
  227.         VSliding = HSliding = FALSE;
  228.     }
  229. }
  230. #endif
  231.  
  232. /* ------------ SCROLL Message -------------- */
  233. static int ScrollMsg(WINDOW wnd, PARAM p1)
  234. {
  235.     /* ---- vertical scroll one line ---- */
  236.     if (p1)    {
  237.         /* ----- scroll one line up ----- */
  238.         if (wnd->wtop+ClientHeight(wnd) >= wnd->wlines)
  239.             return FALSE;
  240.         wnd->wtop++;
  241.     }
  242.     else    {
  243.         /* ----- scroll one line down ----- */
  244.         if (wnd->wtop == 0)
  245.             return FALSE;
  246.         --wnd->wtop;
  247.     }
  248.     if (isVisible(wnd))    {
  249.         RECT rc;
  250.         rc = ClipRectangle(wnd, ClientRect(wnd));
  251.         if (ValidRect(rc))    {
  252.             /* ---- scroll the window ----- */
  253.             scroll_window(wnd, rc, (int)p1);
  254.             if (!(int)p1)
  255.                 /* -- write top line (down) -- */
  256.                 WriteTextLine(wnd,NULL,wnd->wtop,FALSE);
  257.             else    {
  258.                 /* -- write bottom line (up) -- */
  259.                 int y=RectBottom(rc)-GetClientTop(wnd);
  260.                 WriteTextLine(wnd, NULL,
  261.                     wnd->wtop+y, FALSE);
  262.             }
  263.         }
  264. #ifdef INCLUDE_SCROLLBARS
  265.         /* ---- reset the scroll box ---- */
  266.         if (TestAttribute(wnd, VSCROLLBAR))    {
  267.             int vscrollbox = ComputeVScrollBox(wnd);
  268.             if (vscrollbox != wnd->VScrollBox)
  269.                 MoveScrollBox(wnd, vscrollbox);
  270.         }
  271. #endif
  272.         return TRUE;
  273.     }
  274.     return FALSE;
  275. }
  276.  
  277. /* ------------ HORIZSCROLL Message -------------- */
  278. static void HorizScrollMsg(WINDOW wnd, PARAM p1)
  279. {
  280.     /* --- horizontal scroll one column --- */
  281.     if (p1)    {
  282.         /* --- scroll left --- */
  283.         if (wnd->wleft + ClientWidth(wnd)-1 <
  284.                     wnd->textwidth)
  285.             wnd->wleft++;
  286.     }
  287.     else 
  288.         /* --- scroll right --- */
  289.         if (wnd->wleft > 0)
  290.             --wnd->wleft;
  291.     SendMessage(wnd, PAINT, 0, 0);
  292. }
  293.  
  294. /* ------------  SCROLLPAGE Message -------------- */
  295. static void ScrollPageMsg(WINDOW wnd, PARAM p1)
  296. {
  297.     /* --- vertical scroll one page --- */
  298.     if ((int) p1 == FALSE)    {
  299.         /* ---- page up ---- */
  300.         if (wnd->wtop)    {
  301.             wnd->wtop -= ClientHeight(wnd);
  302.             if (wnd->wtop < 0)
  303.                 wnd->wtop = 0;
  304.         }
  305.     }
  306.     else     {
  307.         /* ---- page down ---- */
  308.         if (wnd->wtop+ClientHeight(wnd) < wnd->wlines) {
  309.             wnd->wtop += ClientHeight(wnd);
  310.             if (wnd->wtop>wnd->wlines-ClientHeight(wnd))
  311.                 wnd->wtop=wnd->wlines-ClientHeight(wnd);
  312.         }
  313.     }
  314.     SendMessage(wnd, PAINT, 0, 0);
  315. }
  316.  
  317. /* ------------ HORIZSCROLLPAGE Message -------------- */
  318. static void HorizScrollPageMsg(WINDOW wnd, PARAM p1)
  319. {
  320.     /* --- horizontal scroll one page --- */
  321.     if ((int) p1 == FALSE)    {
  322.         /* ---- page left ----- */
  323.         wnd->wleft -= ClientWidth(wnd);
  324.         if (wnd->wleft < 0)
  325.             wnd->wleft = 0;
  326.     }
  327.     else    {
  328.         /* ---- page right ----- */
  329.         wnd->wleft += ClientWidth(wnd);
  330.         if (wnd->wleft>wnd->textwidth-ClientWidth(wnd))
  331.             wnd->wleft=wnd->textwidth-ClientWidth(wnd);
  332.     }
  333.     SendMessage(wnd, PAINT, 0, 0);
  334. }
  335.  
  336. /* ------------ SCROLLDOC Message -------------- */
  337. static void ScrollDocMsg(WINDOW wnd, PARAM p1)
  338. {
  339.     /* --- scroll to beginning or end of document --- */
  340.     if ((int) p1)
  341.         wnd->wtop = wnd->wleft = 0;
  342.     else if (wnd->wtop+ClientHeight(wnd) < wnd->wlines){
  343.         wnd->wtop = wnd->wlines-ClientHeight(wnd);
  344.         wnd->wleft = 0;
  345.     }
  346.     SendMessage(wnd, PAINT, 0, 0);
  347. }
  348.  
  349. /* ------------ PAINT Message -------------- */
  350. static int PaintMsg(WINDOW wnd, PARAM p1)
  351. {
  352.     /* ------ paint the client area ----- */
  353.     RECT rc, rcc;
  354.     int y;
  355.     char blankline[201];
  356.  
  357.     /* ----- build the rectangle to paint ----- */
  358.     if ((RECT *)p1 == NULL)
  359.         rc=RelativeWindowRect(wnd, WindowRect(wnd));
  360.     else
  361.         rc= *(RECT *)p1;
  362.     if (TestAttribute(wnd, HASBORDER) &&
  363.             RectRight(rc) >= WindowWidth(wnd)-1) {
  364.         if (RectLeft(rc) >= WindowWidth(wnd)-1)
  365.             return FALSE;
  366.         RectRight(rc) = WindowWidth(wnd)-2;
  367.     }
  368.     rcc = AdjustRectangle(wnd, rc);
  369.  
  370.     /* ----- blank line for padding ----- */
  371.     memset(blankline, ' ', SCREENWIDTH);
  372.     blankline[RectRight(rcc)+1] = '\0';
  373.  
  374.     /* ------- each line within rectangle ------ */
  375.     for (y = RectTop(rc); y <= RectBottom(rc); y++){
  376.         int yy;
  377.         /* ---- test outside of Client area ---- */
  378.         if (TestAttribute(wnd,
  379.                     HASBORDER | HASTITLEBAR))    {
  380.             if (y < TopBorderAdj(wnd))
  381.                 continue;
  382.             if (y > WindowHeight(wnd)-2)
  383.                 continue;
  384.         }
  385.         yy = y-TopBorderAdj(wnd);
  386.         if (yy < wnd->wlines-wnd->wtop)
  387.             /* ---- paint a text line ---- */
  388.             WriteTextLine(wnd, &rc,
  389.                         yy+wnd->wtop, FALSE);
  390.         else    {
  391.             /* ---- paint a blank line ---- */
  392.             SetStandardColor(wnd);
  393.             writeline(wnd, blankline+RectLeft(rcc),
  394.                     RectLeft(rcc)+1, y, FALSE);
  395.         }
  396.     }
  397. #ifdef INCLUDE_SCROLLBARS
  398.     /* ------- position the scroll box ------- */
  399.     if (TestAttribute(wnd, VSCROLLBAR|HSCROLLBAR)) {
  400.         int hscrollbox = ComputeHScrollBox(wnd);
  401.         int vscrollbox = ComputeVScrollBox(wnd);
  402.         if (hscrollbox != wnd->HScrollBox ||
  403.                 vscrollbox != wnd->VScrollBox)    {
  404.             wnd->HScrollBox = hscrollbox;
  405.             wnd->VScrollBox = vscrollbox;
  406.             SendMessage(wnd, BORDER, p1, 0);
  407.         }
  408.     }
  409. #endif
  410.     return TRUE;
  411. }
  412.  
  413. /* ------------ CLOSE_WINDOW Message -------------- */
  414. static void CloseWindowMsg(WINDOW wnd)
  415. {
  416.     SendMessage(wnd, CLEARTEXT, 0, 0);
  417.     if (wnd->TextPointers != NULL)    {
  418.         free(wnd->TextPointers);
  419.         wnd->TextPointers = NULL;
  420.     }
  421. }
  422.  
  423. /* ----------- TEXTBOX Message-processing Module ----------- */
  424. int TextBoxProc(WINDOW wnd, MESSAGE msg, PARAM p1, PARAM p2)
  425. {
  426.     switch (msg)    {
  427.         case CREATE_WINDOW:
  428.             wnd->HScrollBox = wnd->VScrollBox = 1;
  429.             ClearTextPointers(wnd);
  430.             break;
  431.         case ADDTEXT:
  432.             AddTextMsg(wnd, p1);
  433.             break;
  434.         case SETTEXT:
  435.             SetTextMsg(wnd, p1);
  436.             break;
  437.         case CLEARTEXT:
  438.             ClearTextMsg(wnd);
  439.             break;
  440.         case KEYBOARD:
  441. #ifdef INCLUDE_SYSTEM_MENUS
  442.             if (WindowMoving || WindowSizing)
  443.                 return FALSE;
  444. #endif
  445.             if (KeyboardMsg(wnd, p1))
  446.                 return TRUE;
  447.             break;
  448.         case LEFT_BUTTON:
  449. #ifdef INCLUDE_SYSTEM_MENUS
  450.             if (WindowSizing || WindowMoving)
  451.                 return FALSE;
  452. #endif
  453. #ifdef INCLUDE_SCROLLBARS
  454.             if (LeftButtonMsg(wnd, p1, p2))
  455.                 return TRUE;
  456.             break;
  457.         case MOUSE_MOVED:
  458.             if (MouseMovedMsg(wnd, p1, p2))
  459.                 return TRUE;
  460.             break;
  461.         case BUTTON_RELEASED:
  462.             ButtonReleasedMsg(wnd);
  463. #endif
  464.             break;
  465.         case SCROLL:
  466.             ScrollMsg(wnd, p1);
  467.             return TRUE;
  468.         case HORIZSCROLL:
  469.             HorizScrollMsg(wnd, p1);
  470.             return TRUE;
  471.         case SCROLLPAGE:
  472.             ScrollPageMsg(wnd, p1);
  473.             return TRUE;
  474.         case HORIZPAGE:
  475.             HorizScrollPageMsg(wnd, p1);
  476.             return TRUE;
  477.         case SCROLLDOC:
  478.             ScrollDocMsg(wnd, p1);
  479.             return TRUE;
  480.         case PAINT:
  481.             if (isVisible(wnd) && wnd->wlines)    {
  482.                 PaintMsg(wnd, p1);
  483.                 return FALSE;
  484.             }
  485.             break;
  486.         case CLOSE_WINDOW:
  487.             CloseWindowMsg(wnd);
  488.             break;
  489.         default:
  490.             break;
  491.     }
  492.     return BaseWndProc(TEXTBOX, wnd, msg, p1, p2);
  493. }
  494.  
  495. #ifdef INCLUDE_SCROLLBARS
  496. /* ------ compute the vertical scroll box position from
  497.                    the text pointers --------- */
  498. static int ComputeVScrollBox(WINDOW wnd)
  499. {
  500.     int pagelen = wnd->wlines - ClientHeight(wnd);
  501.     int barlen = ClientHeight(wnd)-2;
  502.     int lines_tick;
  503.     int vscrollbox;
  504.  
  505.     if (pagelen < 1 || barlen < 1)
  506.         vscrollbox = 1;
  507.     else    {
  508.         if (pagelen > barlen)
  509.             lines_tick = pagelen / barlen;
  510.         else
  511.             lines_tick = barlen / pagelen;
  512.         vscrollbox = 1 + (wnd->wtop / lines_tick);
  513.         if (vscrollbox > ClientHeight(wnd)-2 ||
  514.                 wnd->wtop + ClientHeight(wnd) >= wnd->wlines)
  515.             vscrollbox = ClientHeight(wnd)-2;
  516.     }
  517.     return vscrollbox;
  518. }
  519.  
  520. /* ---- compute top text line from scroll box position ---- */
  521. static void ComputeWindowTop(WINDOW wnd)
  522. {
  523.     int pagelen = wnd->wlines - ClientHeight(wnd);
  524.     if (wnd->VScrollBox == 0)
  525.         wnd->wtop = 0;
  526.     else if (wnd->VScrollBox == ClientHeight(wnd)-2)
  527.         wnd->wtop = pagelen;
  528.     else    {
  529.         int barlen = ClientHeight(wnd)-2;
  530.         int lines_tick;
  531.  
  532.         if (pagelen > barlen)
  533.             lines_tick = pagelen / barlen;
  534.         else
  535.             lines_tick = barlen / pagelen;
  536.         wnd->wtop = (wnd->VScrollBox-1) * lines_tick;
  537.         if (wnd->wtop + ClientHeight(wnd) > wnd->wlines)
  538.             wnd->wtop = pagelen;
  539.     }
  540.     if (wnd->wtop < 0)
  541.         wnd->wtop = 0;
  542. }
  543.  
  544. /* ------ compute the horizontal scroll box position from
  545.                    the text pointers --------- */
  546. static int ComputeHScrollBox(WINDOW wnd)
  547. {
  548.     int pagewidth = wnd->textwidth - ClientWidth(wnd);
  549.     int barlen = ClientWidth(wnd)-2;
  550.     int chars_tick;
  551.     int hscrollbox;
  552.  
  553.     if (pagewidth < 1 || barlen < 1)
  554.         hscrollbox = 1;
  555.     else     {
  556.         if (pagewidth > barlen)
  557.             chars_tick = pagewidth / barlen;
  558.         else
  559.             chars_tick = barlen / pagewidth;
  560.         hscrollbox = 1 + (wnd->wleft / chars_tick);
  561.         if (hscrollbox > ClientWidth(wnd)-2 ||
  562.                 wnd->wleft + ClientWidth(wnd) >= wnd->textwidth)
  563.             hscrollbox = ClientWidth(wnd)-2;
  564.     }
  565.     return hscrollbox;
  566. }
  567.  
  568. /* ---- compute left column from scroll box position ---- */
  569. static void ComputeWindowLeft(WINDOW wnd)
  570. {
  571.     int pagewidth = wnd->textwidth - ClientWidth(wnd);
  572.  
  573.     if (wnd->HScrollBox == 0)
  574.         wnd->wleft = 0;
  575.     else if (wnd->HScrollBox == ClientWidth(wnd)-2)
  576.         wnd->wleft = pagewidth;
  577.     else    {
  578.         int barlen = ClientWidth(wnd)-2;
  579.         int chars_tick;
  580.  
  581.         if (pagewidth > barlen)
  582.             chars_tick = pagewidth / barlen;
  583.         else
  584.             chars_tick = barlen / pagewidth;
  585.         wnd->wleft = (wnd->HScrollBox-1) * chars_tick;
  586.         if (wnd->wleft + ClientWidth(wnd) > wnd->textwidth)
  587.             wnd->wleft = pagewidth;
  588.     }
  589.     if (wnd->wleft < 0)
  590.         wnd->wleft = 0;
  591. }
  592. #endif
  593.  
  594. /* ----- get the text to a specified line ----- */
  595. static char *GetTextLine(WINDOW wnd, int selection)
  596. {
  597.     char *line;
  598.     int len = 0;
  599.     char *cp, *cp1;
  600.     cp = cp1 = TextLine(wnd, selection);
  601.     while (*cp && *cp != '\n')    {
  602.         len++;
  603.         cp++;
  604.     }
  605.     line = malloc(len+6);
  606.     if (line != NULL)    {
  607.         memmove(line, cp1, len);
  608.         line[len] = '\0';
  609.     }
  610.     return line;
  611. }
  612.  
  613. /* ------- write a line of text to a textbox window ------- */
  614. void WriteTextLine(WINDOW wnd, RECT *rcc, int y, int reverse)
  615. {
  616.     int len = 0;
  617.     int dif = 0;
  618.     unsigned char *line;
  619.     RECT rc;
  620.     unsigned char *lp, *svlp;
  621.     int lnlen;
  622.     int i;
  623.     int trunc = FALSE;
  624.  
  625.     /* ------ make sure y is inside the window ----- */
  626.     if (y < wnd->wtop || y >= wnd->wtop+ClientHeight(wnd))
  627.         return;
  628.  
  629.     /* ---- build the retangle within which can write ---- */
  630.     if (rcc == NULL)    {
  631.         rc = RelativeWindowRect(wnd, WindowRect(wnd));
  632.         if (TestAttribute(wnd, HASBORDER) &&
  633.                 RectRight(rc) >= WindowWidth(wnd)-1)
  634.             RectRight(rc) = WindowWidth(wnd)-2;
  635.     }
  636.     else
  637.         rc = *rcc;
  638.  
  639.     /* ----- make sure rectangle is within window ------ */
  640.     if (RectLeft(rc) >= WindowWidth(wnd)-1)
  641.         return;
  642.     if (RectRight(rc) == 0)
  643.         return;
  644.     rc = AdjustRectangle(wnd, rc);
  645.     if (y-wnd->wtop<RectTop(rc) || y-wnd->wtop>RectBottom(rc))
  646.         return;
  647.  
  648.     /* --- get the text and length of the text line --- */
  649.     lp = svlp = GetTextLine(wnd, y);
  650.     if (svlp == NULL)
  651.         return;
  652.     lnlen = LineLength(lp);
  653.  
  654.     /* -------- insert block color change controls ------- */
  655.     if (BlockMarked(wnd))    {
  656.         int bbl = wnd->BlkBegLine;
  657.         int bel = wnd->BlkEndLine;
  658.         int bbc = wnd->BlkBegCol;
  659.         int bec = wnd->BlkEndCol;
  660.         int by = y;
  661.  
  662.         /* ----- put lowest marker first ----- */
  663.         if (bbl > bel)    {
  664.             swap(bbl, bel);
  665.             swap(bbc, bec);
  666.         }
  667.         if (bbl == bel && bbc > bec)
  668.             swap(bbc, bec);
  669.  
  670.         if (by >= bbl && by <= bel)    {
  671.             /* ------ the block includes this line ----- */
  672.             int blkbeg = 0;
  673.             int blkend = lnlen;
  674.             if (!(by > bbl && by < bel))    {
  675.                 /* --- the entire line is not in the block -- */
  676.                 if (by == bbl)
  677.                     /* ---- the block begins on this line --- */
  678.                     blkbeg = bbc;
  679.                 if (by == bel)
  680.                     /* ---- the block ends on this line ---- */
  681.                     blkend = bec;
  682.             }
  683.             /* ----- insert the reset color token ----- */
  684.             memmove(lp+blkend+1,lp+blkend,strlen(lp+blkend)+1);
  685.             lp[blkend] = RESETCOLOR;
  686.             /* ----- insert the change color token ----- */
  687.             memmove(lp+blkbeg+3,lp+blkbeg,strlen(lp+blkbeg)+1);
  688.             lp[blkbeg] = CHANGECOLOR;
  689.             /* ----- insert the color tokens ----- */
  690.             SetReverseColor(wnd);
  691.             lp[blkbeg+1] = foreground | 0x80;
  692.             lp[blkbeg+2] = background | 0x80;
  693.             lnlen += 4;
  694.         }
  695.     }
  696.     /* - make sure left margin doesn't overlap color change - */
  697.     for (i = 0; i < wnd->wleft+3; i++)    {
  698.         if (*(lp+i) == '\0')
  699.             break;
  700.         if (*(unsigned char *)(lp + i) == RESETCOLOR)
  701.             break;
  702.     }
  703.     if (*(lp+i) && i < wnd->wleft+3)    {
  704.         if (wnd->wleft+4 > lnlen)
  705.             trunc = TRUE;
  706.         else 
  707.             lp += 4;
  708.     }
  709.     else     {
  710.         /* --- it does, shift the color change over --- */
  711.         for (i = 0; i < wnd->wleft; i++)    {
  712.             if (*(lp+i) == '\0')
  713.                 break;
  714.             if (*(unsigned char *)(lp + i) == CHANGECOLOR)    {
  715.                 *(lp+wnd->wleft+2) = *(lp+i+2);
  716.                 *(lp+wnd->wleft+1) = *(lp+i+1);
  717.                 *(lp+wnd->wleft) = *(lp+i);
  718.                 break;
  719.             }
  720.         }
  721.     }
  722.     /* ------ build the line to display -------- */
  723.     if ((line = malloc(200)) != NULL)    {
  724.         if (!trunc)    {
  725.             if (lnlen < wnd->wleft)
  726.                 lnlen = 0;
  727.             else
  728.                 lp += wnd->wleft;
  729.             if (lnlen > RectLeft(rc))    {
  730.                 /* ---- the line exceeds the rectangle ---- */
  731.                 int ct = RectLeft(rc);
  732.                 char *initlp = lp;
  733.                 /* --- point to end of clipped line --- */
  734.                 while (ct)    {
  735.                     if (*(unsigned char *)lp == CHANGECOLOR)
  736.                         lp += 3;
  737.                     else if (*(unsigned char *)lp == RESETCOLOR)
  738.                         lp++;
  739.                     else
  740.                         lp++, --ct;
  741.                 }
  742.                 if (RectLeft(rc))    {
  743.                     char *lpp = lp;
  744.                     while (*lpp)    {
  745.                         if (*(unsigned char*)lpp==CHANGECOLOR)
  746.                             break;
  747.                         if (*(unsigned char*)lpp==RESETCOLOR) {
  748.                             lpp = lp;
  749.                             while (lpp >= initlp)    {
  750.                                 if (*(unsigned char *)lpp ==
  751.                                                 CHANGECOLOR) {
  752.                                     lp -= 3;
  753.                                     memmove(lp,lpp,3);
  754.                                     break;
  755.                                 }
  756.                                 --lpp;
  757.                             }
  758.                             break;
  759.                         }
  760.                         lpp++;
  761.                     }
  762.                 }
  763.                 lnlen = LineLength(lp);
  764.                 len = min(lnlen, RectWidth(rc));
  765.                 dif = strlen(lp) - lnlen;
  766.                 len += dif;
  767.                 if (len > 0)
  768.                     strncpy(line, lp, len);
  769.             }
  770.         }
  771.         /* -------- pad the line --------- */
  772.         while (len < RectWidth(rc)+dif)
  773.             line[len++] = ' ';
  774.         line[len] = '\0';
  775.         dif = 0;
  776.         /* ------ establish the line's main color ----- */
  777.         if (reverse)    {
  778.             char *cp = line;
  779.             SetReverseColor(wnd);
  780.             while ((cp = strchr(cp, CHANGECOLOR)) != NULL)    {
  781.                 cp += 2;
  782.                 *cp++ = background | 0x80;
  783.             }
  784.             if (*(unsigned char *)line == CHANGECOLOR)
  785.                 dif = 3;
  786.         }
  787.         else
  788.             SetStandardColor(wnd);
  789.         /* ------- display the line -------- */
  790.         writeline(wnd, line+dif,
  791.                     RectLeft(rc)+BorderAdj(wnd),
  792.                         y-wnd->wtop+TopBorderAdj(wnd), FALSE);
  793.         free(line);
  794.     }
  795.     free(svlp);
  796. }
  797.  
  798. /* ----- set anchor point for marking text block ----- */
  799. void SetAnchor(WINDOW wnd, int mx, int my)
  800. {
  801.     if (BlockMarked(wnd))    {
  802.         ClearBlock(wnd);
  803.         SendMessage(wnd, PAINT, 0, 0);
  804.     }
  805.     /* ------ set the anchor ------ */
  806.     wnd->BlkBegLine = wnd->BlkEndLine = my;
  807.     wnd->BlkBegCol = wnd->BlkEndCol = mx;
  808. }
  809.  
  810. /* ----- clear and initialize text line pointer array ----- */
  811. void ClearTextPointers(WINDOW wnd)
  812. {
  813.     wnd->TextPointers = realloc(wnd->TextPointers, sizeof(int));
  814.     if (wnd->TextPointers != NULL)
  815.         *(wnd->TextPointers) = 0;
  816. }
  817.  
  818. #define INITLINES 100
  819.  
  820. /* ---- build array of pointers to text lines ---- */
  821. void BuildTextPointers(WINDOW wnd)
  822. {
  823.     char *cp = wnd->text, *cp1;
  824.     int incrs = INITLINES;
  825.     unsigned int off;
  826.     wnd->textwidth = wnd->wlines = 0;
  827.     while (*cp)    {
  828.         if (incrs == INITLINES)    {
  829.             incrs = 0;
  830.             wnd->TextPointers = realloc(wnd->TextPointers,
  831.                     (wnd->wlines + INITLINES) * sizeof(int));
  832.             if (wnd->TextPointers == NULL)
  833.                 break;
  834.         }
  835.         off = (unsigned int) (cp - wnd->text);
  836.         *((wnd->TextPointers) + wnd->wlines) = off;
  837.         wnd->wlines++;
  838.         incrs++;
  839.         cp1 = cp;
  840.         while (*cp && *cp != '\n')
  841.             cp++;
  842.         wnd->textwidth = max(wnd->textwidth,
  843.                         (unsigned int) (cp - cp1));
  844.         if (*cp)
  845.             cp++;
  846.     }
  847. }
  848.  
  849. #ifdef INCLUDE_SCROLLBARS
  850. static void MoveScrollBox(WINDOW wnd, int vscrollbox)
  851. {
  852.     foreground = FrameForeground(wnd);
  853.     background = FrameBackground(wnd);
  854.     PutWindowChar(wnd, WindowWidth(wnd)-1,
  855.             wnd->VScrollBox+1, SCROLLBARCHAR);
  856.     PutWindowChar(wnd, WindowWidth(wnd)-1,
  857.             vscrollbox+1, SCROLLBOXCHAR);
  858.     wnd->VScrollBox = vscrollbox;
  859. }
  860. #endif
  861.